/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// Interleaving of MP3 ADUs
// Implementation

#include "include/MP3ADUinterleaving.hh"
#include "MP3ADUdescriptor.hh"

#include <string.h>

#ifdef TEST_LOSS
#include "GroupsockHelper.hh"
#endif

////////// Interleaving //////////

Interleaving::Interleaving(unsigned cycleSize,
                           unsigned char const *cycleArray)
        : fCycleSize(cycleSize) {
    for (unsigned i = 0; i < fCycleSize; ++i) {
        fInverseCycle[cycleArray[i]] = i;
    }
}

Interleaving::~Interleaving() {
}

////////// MP3ADUinterleaverBase //////////

MP3ADUinterleaverBase::MP3ADUinterleaverBase(UsageEnvironment &env,
                                             FramedSource *inputSource)
        : FramedFilter(env, inputSource) {
}

MP3ADUinterleaverBase::~MP3ADUinterleaverBase() {
}

FramedSource *MP3ADUinterleaverBase::getInputSource(UsageEnvironment &env,
                                                    char const *inputSourceName) {
    FramedSource *inputSource;
    if (!FramedSource::lookupByName(env, inputSourceName, inputSource))
        return NULL;

    if (strcmp(inputSource->MIMEtype(), "audio/MPA-ROBUST") != 0) {
        env.setResultMsg(inputSourceName, " is not an MP3 ADU source");
        return NULL;
    }

    return inputSource;
}

void MP3ADUinterleaverBase::afterGettingFrame(void *clientData,
                                              unsigned numBytesRead,
                                              unsigned /*numTruncatedBytes*/,
                                              struct timeval presentationTime,
                                              unsigned durationInMicroseconds) {
    MP3ADUinterleaverBase *interleaverBase = (MP3ADUinterleaverBase *) clientData;
    // Finish up after reading:
    interleaverBase->afterGettingFrame(numBytesRead,
                                       presentationTime, durationInMicroseconds);

    // Then, continue to deliver an outgoing frame:
    interleaverBase->doGetNextFrame();
}


////////// InterleavingFrames (definition) //////////

class InterleavingFrames {
public:
    InterleavingFrames(unsigned maxCycleSize);

    virtual ~InterleavingFrames();

    Boolean haveReleaseableFrame();

    void getIncomingFrameParams(unsigned char index,
                                unsigned char *&dataPtr,
                                unsigned &bytesAvailable);

    void getReleasingFrameParams(unsigned char index,
                                 unsigned char *&dataPtr,
                                 unsigned &bytesInUse,
                                 struct timeval &presentationTime,
                                 unsigned &durationInMicroseconds);

    void setFrameParams(unsigned char index,
                        unsigned char icc, unsigned char ii,
                        unsigned frameSize, struct timeval presentationTime,
                        unsigned durationInMicroseconds);

    unsigned nextIndexToRelease() { return fNextIndexToRelease; }

    void releaseNext();

private:
    unsigned fMaxCycleSize;
    unsigned fNextIndexToRelease;

    class InterleavingFrameDescriptor *fDescriptors;
};

////////// MP3ADUinterleaver //////////


MP3ADUinterleaver::MP3ADUinterleaver(UsageEnvironment &env,
                                     Interleaving const &interleaving,
                                     FramedSource *inputSource)
        : MP3ADUinterleaverBase(env, inputSource),
          fInterleaving(interleaving),
          fFrames(new InterleavingFrames(interleaving.cycleSize())),
          fII(0), fICC(0) {
}

MP3ADUinterleaver::~MP3ADUinterleaver() {
    delete fFrames;
}

MP3ADUinterleaver *MP3ADUinterleaver::createNew(UsageEnvironment &env,
                                                Interleaving const &interleaving,
                                                FramedSource *inputSource) {
    return new MP3ADUinterleaver(env, interleaving, inputSource);
}

void MP3ADUinterleaver::doGetNextFrame() {
    // If there's a frame immediately available, deliver it, otherwise get new
    // frames from the source until one's available:
    if (fFrames->haveReleaseableFrame()) {
        releaseOutgoingFrame();

        // Call our own 'after getting' function.  Because we're not a 'leaf'
        // source, we can call this directly, without risking infinite recursion.
        afterGetting(this);
    } else {
        fPositionOfNextIncomingFrame = fInterleaving.lookupInverseCycle(fII);
        unsigned char *dataPtr;
        unsigned bytesAvailable;
        fFrames->getIncomingFrameParams(fPositionOfNextIncomingFrame,
                                        dataPtr, bytesAvailable);

        // Read the next incoming frame (asynchronously)
        fInputSource->getNextFrame(dataPtr, bytesAvailable,
                                   &MP3ADUinterleaverBase::afterGettingFrame, this,
                                   handleClosure, this);
    }
}

void MP3ADUinterleaver::releaseOutgoingFrame() {
    unsigned char *fromPtr;
    fFrames->getReleasingFrameParams(fFrames->nextIndexToRelease(),
                                     fromPtr, fFrameSize,
                                     fPresentationTime, fDurationInMicroseconds);

    if (fFrameSize > fMaxSize) {
        fNumTruncatedBytes = fFrameSize - fMaxSize;
        fFrameSize = fMaxSize;
    }
    memmove(fTo, fromPtr, fFrameSize);

    fFrames->releaseNext();
}

void MP3ADUinterleaver::afterGettingFrame(unsigned numBytesRead,
                                          struct timeval presentationTime,
                                          unsigned durationInMicroseconds) {
    // Set the (icc,ii) and frame size of the newly-read frame:
    fFrames->setFrameParams(fPositionOfNextIncomingFrame,
                            fICC, fII, numBytesRead,
                            presentationTime, durationInMicroseconds);

    // Prepare our counters for the next frame:
    if (++fII == fInterleaving.cycleSize()) {
        fII = 0;
        fICC = (fICC + 1) % 8;
    }
}

////////// DeinterleavingFrames (definition) //////////

class DeinterleavingFrames {
public:
    DeinterleavingFrames();

    virtual ~DeinterleavingFrames();

    Boolean haveReleaseableFrame();

    void getIncomingFrameParams(unsigned char *&dataPtr,
                                unsigned &bytesAvailable);

    void getIncomingFrameParamsAfter(unsigned frameSize,
                                     struct timeval presentationTime,
                                     unsigned durationInMicroseconds,
                                     unsigned char &icc, unsigned char &ii);

    void getReleasingFrameParams(unsigned char *&dataPtr,
                                 unsigned &bytesInUse,
                                 struct timeval &presentationTime,
                                 unsigned &durationInMicroseconds);

    void moveIncomingFrameIntoPlace();

    void releaseNext();

    void startNewCycle();

private:
    unsigned fNextIndexToRelease;
    Boolean fHaveEndedCycle;
    unsigned fIIlastSeen;
    unsigned fMinIndexSeen, fMaxIndexSeen; // actually, max+1
    class DeinterleavingFrameDescriptor *fDescriptors;
};

////////// MP3ADUdeinterleaver //////////

MP3ADUdeinterleaver::MP3ADUdeinterleaver(UsageEnvironment &env,
                                         FramedSource *inputSource)
        : MP3ADUinterleaverBase(env, inputSource),
          fFrames(new DeinterleavingFrames),
          fIIlastSeen(~0), fICClastSeen(~0) {
}

MP3ADUdeinterleaver::~MP3ADUdeinterleaver() {
    delete fFrames;
}

MP3ADUdeinterleaver *MP3ADUdeinterleaver::createNew(UsageEnvironment &env,
                                                    FramedSource *inputSource) {
    return new MP3ADUdeinterleaver(env, inputSource);
}

void MP3ADUdeinterleaver::doGetNextFrame() {
    // If there's a frame immediately available, deliver it, otherwise get new
    // frames from the source until one's available:
    if (fFrames->haveReleaseableFrame()) {
        releaseOutgoingFrame();

        // Call our own 'after getting' function.  Because we're not a 'leaf'
        // source, we can call this directly, without risking infinite recursion.
        afterGetting(this);
    } else {
#ifdef TEST_LOSS
        NOTE: This code no longer works, because it uses synchronous reads,
        which are no longer supported.
          static unsigned const framesPerPacket = 3;
          static unsigned const frameCount = 0;
          static Boolean packetIsLost;
          while (1) {
            unsigned packetCount = frameCount/framesPerPacket;
            if ((frameCount++)%framesPerPacket == 0) {
          packetIsLost = (our_random()%10 == 0); // simulate 10% packet loss #####
            }

            if (packetIsLost) {
          // Read and discard the next input frame (that would be part of
          // a lost packet):
          unsigned char dummyBuf[2000];
          unsigned numBytesRead;
          struct timeval presentationTime;
          // (this works only if the source can be read synchronously)
          fInputSource->syncGetNextFrame(dummyBuf, sizeof dummyBuf,
                             numBytesRead, presentationTime);
            } else {
          break; // from while (1)
            }
          }
#endif
        unsigned char *dataPtr;
        unsigned bytesAvailable;
        fFrames->getIncomingFrameParams(dataPtr, bytesAvailable);

        // Read the next incoming frame (asynchronously)
        fInputSource->getNextFrame(dataPtr, bytesAvailable,
                                   &MP3ADUinterleaverBase::afterGettingFrame, this,
                                   handleClosure, this);
    }
}

void MP3ADUdeinterleaver::afterGettingFrame(unsigned numBytesRead,
                                            struct timeval presentationTime,
                                            unsigned durationInMicroseconds) {
    // Get the (icc,ii) and set the frame size of the newly-read frame:
    unsigned char icc, ii;
    fFrames->getIncomingFrameParamsAfter(numBytesRead,
                                         presentationTime, durationInMicroseconds,
                                         icc, ii);

    // Compare these to the values we saw last:
    if (icc != fICClastSeen || ii == fIIlastSeen) {
        // We've started a new interleave cycle
        // (or interleaving was not used).  Release all
        // pending ADU frames to the ADU->MP3 conversion step:
        fFrames->startNewCycle();
    } else {
        // We're still in the same cycle as before.
        // Move the newly-read frame into place, so it can be used:
        fFrames->moveIncomingFrameIntoPlace();
    }

    fICClastSeen = icc;
    fIIlastSeen = ii;
}

void MP3ADUdeinterleaver::releaseOutgoingFrame() {
    unsigned char *fromPtr;
    fFrames->getReleasingFrameParams(fromPtr, fFrameSize,
                                     fPresentationTime, fDurationInMicroseconds);

    if (fFrameSize > fMaxSize) {
        fNumTruncatedBytes = fFrameSize - fMaxSize;
        fFrameSize = fMaxSize;
    }
    memmove(fTo, fromPtr, fFrameSize);

    fFrames->releaseNext();
}

////////// InterleavingFrames (implementation) //////////

#define MAX_FRAME_SIZE 2000 /* conservatively high */

class InterleavingFrameDescriptor {
public:
    InterleavingFrameDescriptor() { frameDataSize = 0; }

    unsigned frameDataSize; // includes ADU descriptor and (modified) MPEG hdr
    struct timeval presentationTime;
    unsigned durationInMicroseconds;
    unsigned char frameData[MAX_FRAME_SIZE]; // ditto
};

InterleavingFrames::InterleavingFrames(unsigned maxCycleSize)
        : fMaxCycleSize(maxCycleSize), fNextIndexToRelease(0),
          fDescriptors(new InterleavingFrameDescriptor[maxCycleSize]) {
}

InterleavingFrames::~InterleavingFrames() {
    delete[] fDescriptors;
}

Boolean InterleavingFrames::haveReleaseableFrame() {
    return fDescriptors[fNextIndexToRelease].frameDataSize > 0;
}

void InterleavingFrames::getIncomingFrameParams(unsigned char index,
                                                unsigned char *&dataPtr,
                                                unsigned &bytesAvailable) {
    InterleavingFrameDescriptor &desc = fDescriptors[index];
    dataPtr = &desc.frameData[0];
    bytesAvailable = MAX_FRAME_SIZE;
}

void InterleavingFrames::getReleasingFrameParams(unsigned char index,
                                                 unsigned char *&dataPtr,
                                                 unsigned &bytesInUse,
                                                 struct timeval &presentationTime,
                                                 unsigned &durationInMicroseconds) {
    InterleavingFrameDescriptor &desc = fDescriptors[index];
    dataPtr = &desc.frameData[0];
    bytesInUse = desc.frameDataSize;
    presentationTime = desc.presentationTime;
    durationInMicroseconds = desc.durationInMicroseconds;
}

void InterleavingFrames::setFrameParams(unsigned char index,
                                        unsigned char icc,
                                        unsigned char ii,
                                        unsigned frameSize,
                                        struct timeval presentationTime,
                                        unsigned durationInMicroseconds) {
    InterleavingFrameDescriptor &desc = fDescriptors[index];
    desc.frameDataSize = frameSize;
    desc.presentationTime = presentationTime;
    desc.durationInMicroseconds = durationInMicroseconds;

    // Advance over the ADU descriptor, to get to the MPEG 'syncword':
    unsigned char *ptr = &desc.frameData[0];
    (void) ADUdescriptor::getRemainingFrameSize(ptr);

    // Replace the next 11 bits with (ii,icc):
    *ptr++ = ii;
    *ptr &= ~0xE0;
    *ptr |= (icc << 5);
}

void InterleavingFrames::releaseNext() {
    fDescriptors[fNextIndexToRelease].frameDataSize = 0;
    fNextIndexToRelease = (fNextIndexToRelease + 1) % fMaxCycleSize;
}

////////// DeinterleavingFrames (implementation) //////////

class DeinterleavingFrameDescriptor {
public:
    DeinterleavingFrameDescriptor() {
        frameDataSize = 0;
        frameData = NULL;
    }

    virtual ~DeinterleavingFrameDescriptor() { delete[] frameData; }

    unsigned frameDataSize; // includes ADU descriptor and (modified) MPEG hdr
    struct timeval presentationTime;
    unsigned durationInMicroseconds;
    unsigned char *frameData;
};

DeinterleavingFrames::DeinterleavingFrames()
        : fNextIndexToRelease(0), fHaveEndedCycle(False),
          fMinIndexSeen(MAX_CYCLE_SIZE), fMaxIndexSeen(0),
          fDescriptors(new DeinterleavingFrameDescriptor[MAX_CYCLE_SIZE + 1]) {
}

DeinterleavingFrames::~DeinterleavingFrames() {
    delete[] fDescriptors;
}

Boolean DeinterleavingFrames::haveReleaseableFrame() {
    if (!fHaveEndedCycle) {
        // Check just the next frame in the sequence
        return fDescriptors[fNextIndexToRelease].frameDataSize > 0;
    } else {
        // We've just ended a cycle, so we can skip over frames that didn't
        // get filled in (due to packet loss):
        if (fNextIndexToRelease < fMinIndexSeen) {
            fNextIndexToRelease = fMinIndexSeen;
        }
        while (fNextIndexToRelease < fMaxIndexSeen
               && fDescriptors[fNextIndexToRelease].frameDataSize == 0) {
            ++fNextIndexToRelease;
        }
        if (fNextIndexToRelease >= fMaxIndexSeen) {
            // No more frames are available from the cycle that we just ended, so
            // clear out all previously stored frames, then make available
            // the last-read frame, and return false for now:
            for (unsigned i = fMinIndexSeen; i < fMaxIndexSeen; ++i) {
                fDescriptors[i].frameDataSize = 0;
            }

            fMinIndexSeen = MAX_CYCLE_SIZE;
            fMaxIndexSeen = 0;
            moveIncomingFrameIntoPlace();

            fHaveEndedCycle = False;
            fNextIndexToRelease = 0;
            return False;
        }

        return True;
    }
}

void DeinterleavingFrames::getIncomingFrameParams(unsigned char *&dataPtr,
                                                  unsigned &bytesAvailable) {
    // Use fDescriptors[MAX_CYCLE_SIZE] to store the incoming frame,
    // prior to figuring out its real position:
    DeinterleavingFrameDescriptor &desc = fDescriptors[MAX_CYCLE_SIZE];
    if (desc.frameData == NULL) {
        // There's no buffer yet, so allocate a new one:
        desc.frameData = new unsigned char[MAX_FRAME_SIZE];
    }
    dataPtr = desc.frameData;
    bytesAvailable = MAX_FRAME_SIZE;
}

void DeinterleavingFrames
::getIncomingFrameParamsAfter(unsigned frameSize,
                              struct timeval presentationTime,
                              unsigned durationInMicroseconds,
                              unsigned char &icc, unsigned char &ii) {
    DeinterleavingFrameDescriptor &desc = fDescriptors[MAX_CYCLE_SIZE];
    desc.frameDataSize = frameSize;
    desc.presentationTime = presentationTime;
    desc.durationInMicroseconds = durationInMicroseconds;

    // Advance over the ADU descriptor, to get to the MPEG 'syncword':
    unsigned char *ptr = desc.frameData;
    (void) ADUdescriptor::getRemainingFrameSize(ptr);

    // Read the next 11 bits into (ii,icc), and replace them with all-1s:
    fIIlastSeen = ii = *ptr;
    *ptr++ = 0xFF;
    icc = (*ptr & 0xE0) >> 5;
    *ptr |= 0xE0;
}

void DeinterleavingFrames::getReleasingFrameParams(unsigned char *&dataPtr,
                                                   unsigned &bytesInUse,
                                                   struct timeval &presentationTime,
                                                   unsigned &durationInMicroseconds) {
    DeinterleavingFrameDescriptor &desc = fDescriptors[fNextIndexToRelease];
    dataPtr = desc.frameData;
    bytesInUse = desc.frameDataSize;
    presentationTime = desc.presentationTime;
    durationInMicroseconds = desc.durationInMicroseconds;
}

void DeinterleavingFrames::moveIncomingFrameIntoPlace() {
    DeinterleavingFrameDescriptor &fromDesc = fDescriptors[MAX_CYCLE_SIZE];
    DeinterleavingFrameDescriptor &toDesc = fDescriptors[fIIlastSeen];

    toDesc.frameDataSize = fromDesc.frameDataSize;
    toDesc.presentationTime = fromDesc.presentationTime;

    // Move the data pointer into place by swapping the data pointers:
    unsigned char *tmp = toDesc.frameData;
    toDesc.frameData = fromDesc.frameData;
    fromDesc.frameData = tmp;

    if (fIIlastSeen < fMinIndexSeen) {
        fMinIndexSeen = fIIlastSeen;
    }
    if (fIIlastSeen + 1 > fMaxIndexSeen) {
        fMaxIndexSeen = fIIlastSeen + 1;
    }
}

void DeinterleavingFrames::releaseNext() {
    fDescriptors[fNextIndexToRelease].frameDataSize = 0;
    fNextIndexToRelease = (fNextIndexToRelease + 1) % MAX_CYCLE_SIZE;
}

void DeinterleavingFrames::startNewCycle() {
    fHaveEndedCycle = True;
}
